// 第一次调用 $mount(el) oldVnode 为根 dom，即 #app, 其后每次都无参调用 $mount(),oldVnode 为 undefined
export function patch(oldVnode, vnode) {
  if (!oldVnode) {
    return createElm(vnode);
  }

  // 首次渲染时 oldVnode 是一个真实 dom , 后续的渲染就是 vnode, 使用 diff 算法更新
  const isRealElement = oldVnode.nodeType;

  if (isRealElement) {
    // 首次渲染, 直接替换
    const oldElm = oldVnode;
    const parentElm = oldVnode.parentNode;
    const el = createElm(vnode);
    parentElm.insertBefore(el, oldElm.nextSibling); // 插入到其后一个兄弟节点的前面，即插入到原节点后面,当第二个参数为 null 时直接添加
    // parentElm.insertBefore(el, oldElm); // 直接插入到原节点前面，效果与上面一样
    parentElm.removeChild(oldElm);
    return el;
  } else {
    // 后续更新使用 diff 算法
  }
}

function createComponent(vnode) {
  let i = vnode.data;
  // 在 ./vdom/index.js 中 给 data 增加 hook.init 属性, 给vnode 增加 vnode.componentInstance

  if ((i = i.hook) && (i = i.init)) {
    i(vnode); // 调用组件的初始化方法
  }
  if (vnode.componentInstance) return true;
  return false;
}

function createElm(vnode) {
  let { tag, children, key, data, text, vm } = vnode;
  if (typeof tag === "string") {
    if (createComponent(vnode)) {
      // 为 true 说明此虚拟节点是组件，将组件渲染后将真实节点返回
      return vnode.componentInstance.$el;
    }

    // 以下为非组件节点的操作
    vnode.el = document.createElement(tag); // 得到真实节点
    updateProperties(vnode); // 对属性更新
    if (children.length) {
      children.forEach((child) => {
        let el = createElm(child);
        vnode.el.appendChild(el);
      });
    }
  } else {
    // 文本节点的 tag 为 undefined
    vnode.el = document.createTextNode(text);
  }
  return vnode.el;
}

function updateProperties(vnode) {
  const newProps = vnode.data || {};
  const el = vnode.el;
  for (let attr in newProps) {
    if (newProps.hasOwnProperty(attr)) {
      if (attr === "style") {
        for (let styleName in newProps.style) {
          el.style[styleName] = newProps.style[styleName];
        }
      } else if (attr === "class") {
        // class 实际只会有一个，可以直接赋值
        el.className = newProps[attr];
      } else {
        el.setAttribute(attr, newProps[attr]);
      }
    }
  }
}
